package com.gitee.wsl.ext.list


import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.max

/**
 * For each child [ArrayList] contained in this [ArrayList], replaces the elements of the child [ArrayList] with the
 * elements of the corresponding [Collection] from the provided [List]. The child [ArrayList] and [Collection] are
 * associated by index. If a given child [ArrayList] contained in this [ArrayList] has no corresponding [Collection] in
 * the provided [List], the child [ArrayList] will be cleared. If the size of this [ArrayList] is smaller than the size
 * of the provided [List], an appropriate number of empty child [ArrayList]s will first be added to this [ArrayList].
 */
fun <T> ArrayList<ArrayList<T>>.setToAllChildren(other: List<Collection<T>>) {
    ensureSize(other.size)
    forEachIndexed { index, childArrayList ->
        childArrayList.clear()
        if (other.lastIndex >= index) {
            childArrayList.addAll(other[index])
        }
    }
}

val List<*>.lastIndex: Int get() = max(0,size - 1)

/**
 * Creates a [MutableList] containing all elements of the specified source collection.
 */
fun <T> mutableListOf(sourceCollection: Collection<T>): MutableList<T> = ArrayList<T>(sourceCollection.size).apply { addAll(sourceCollection) }

fun <T> List<T>.toImmutableList(): List<T> = when (size) {
    0 -> emptyList()
    1 -> listOf( first())
    else -> ArrayList(this)
}

fun <T> List<T>.notInIndex(indexCheck: Int) :Boolean = indexCheck !in indices

fun <T> List<T>.useWithIndex(indexCheck: Int, action: (T) -> Unit) {
    if (indexCheck in indices) {
        action(this[indexCheck])
    }
}

 fun <T> List<T>.useLast(action: (T) -> Unit) = useWithIndex(lastIndex, action)

 fun <T> List<T>.useFirst(action: (T) -> Unit) = useWithIndex(0, action)


fun <T> MutableList<T>.add(pair:Pair<T,T>){
    add(pair.first)
    add(pair.second)
}

/*fun <T> MutableList<T>.addAll(pair:Pair<T,T>){
    add(pair.first)
    add(pair.second)
}*/


fun <T> List<T>.updatedAt(index: Int, value: T): List<T> {
    val result = toMutableList()
    result[index] = value
    return result
}


//fun <T> MutableList<T>.hasMore() = this.isNotEmpty()

/**
 * Replaces all of the elements of this [MutableList] with the elements of the provided collection.
 */
fun <T> MutableList<T>.setAll(other: Collection<T>) {
    clear()
    addAll(other)
}

/**
 * Replaces all of the elements of this [MutableList] with the elements of the provided array.
 */
fun <T> MutableList<T>.setAll(other: Array<out T>) {
    clear()
    addAll(other)
}

inline fun <reified T> l(vararg values:T) = values.toList()

/**
 * An optimised variant of [listOfNotNull].
 *
 * The method will create a new list and add all elements that are not null.
 *
 * @param elements The elements to add to the list
 * @return A list of non-null elements
 * @see listOfNotNull
 */
fun <T> notNullListOf(vararg elements: T?): List<T> {
    val list = mutableListOf<T>()
    elements.forEach { if (it != null) list.add(it) }
    return list
}



fun <E> MutableList<E>.addOrRemove(element: E) {
    if(contains(element))
        remove(element)
    else
        add(element)
}

suspend fun <T> List<T>.forEach(parallelism: Int = 10, processBlock: suspend (T) -> Unit) {
    withContext(Dispatchers.Default) {
        val inputChannel = Channel <T>(parallelism)

        launch {
            forEach {item->
                inputChannel.send(item)
            }
            inputChannel.close()
        }

        for (i in 0..parallelism) launch {
            for (element in inputChannel) {
                processBlock(element)
            }
        }
    }
}


fun <T> List<T>.breakAsPairs(): List<Pair<T, T>> {
    val result = mutableListOf<Pair<T, T>>()

    for (i in 0 until size - 1) {
        val first = get(i)
        val second = get(i + 1)
        result.add(first to second)
    }

    return result
}

inline fun <reified T> List<*>.typeOf(): List<T> {
    val retlist = mutableListOf<T>()
    this.forEach {
        if (it is T) {
            retlist.add(it)
        }
    }
    return retlist
}


